/*
 * i2c.c
 *
 *  Created on: May 13, 2023
 *      Author: zhulijun
 */

#include "my_inc.h"



typedef struct i2c_gpio_s
{
	GPIO_TypeDef       *group;
	uint16_t            SCL;	//PB6
	uint16_t            SDA;	//PB7
}i2c_gpio_t;

static i2c_gpio_t  i2c_pins =
{ .group = GPIOB,
  .SCL = GPIO_PIN_6,
  .SDA = GPIO_PIN_7,
};


#define SDA_IN()   do{  GPIO_InitTypeDef GPIO_InitStruct = {0};\
							GPIO_InitStruct.Pin = i2c_pins.SDA;   \
							GPIO_InitStruct.Mode = GPIO_MODE_INPUT;   \
							GPIO_InitStruct.Pull =GPIO_PULLUP;   \
							GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_HIGH;  \
							HAL_GPIO_Init(i2c_pins.group, &GPIO_InitStruct);   \
					}while(0)

#define SDA_OUT()   do{  GPIO_InitTypeDef GPIO_InitStruct = {0};\
							GPIO_InitStruct.Pin = i2c_pins.SDA;   \
							GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;   \
							GPIO_InitStruct.Pull =GPIO_PULLUP;   \
							GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_HIGH;  \
							HAL_GPIO_Init(i2c_pins.group, &GPIO_InitStruct);   \
					}while(0)

#define SCL_OUT()   do{  GPIO_InitTypeDef GPIO_InitStruct = {0};\
							GPIO_InitStruct.Pin = i2c_pins.SCL;   \
							GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;   \
							GPIO_InitStruct.Pull =GPIO_PULLUP;   \
							GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_HIGH;  \
							HAL_GPIO_Init(i2c_pins.group, &GPIO_InitStruct);   \
					}while(0)



#define SCL_HIGH()   	HAL_GPIO_WritePin(i2c_pins.group, i2c_pins.SCL, GPIO_PIN_SET)
#define SDA_HIGH()	    HAL_GPIO_WritePin(i2c_pins.group, i2c_pins.SDA, GPIO_PIN_SET)
#define SCL_LOW()   	HAL_GPIO_WritePin(i2c_pins.group, i2c_pins.SCL, GPIO_PIN_RESET)
#define SDA_LOW()		HAL_GPIO_WritePin(i2c_pins.group, i2c_pins.SDA, GPIO_PIN_RESET)

#define READ_SCL()  	HAL_GPIO_ReadPin(i2c_pins.group, i2c_pins.SCL)
#define READ_SDA()  	HAL_GPIO_ReadPin(i2c_pins.group, i2c_pins.SDA)




/*==========================================
	函数名称：IIC_Init
	函数功能：IIC 初始化操作
===========================================*/
void IIC_Init()
{
	__HAL_RCC_GPIOB_CLK_ENABLE();

	SDA_OUT();
	SCL_OUT();

	SCL_HIGH();
	SDA_HIGH();

}


/*==========================================
	函数名称：IIC_Start
	函数功能：IIC 起始信号
===========================================*/
void IIC_Start(void)
{
	SDA_OUT();		//SDA 输出模式(看图IIC工作图来操作)

	SDA_HIGH();
	SCL_HIGH();
	delay_us(5);

	SDA_LOW();
	delay_us(5);

	SCL_LOW();			//钳住SCL 让 SDA 拉低有效
}

/*==========================================
	函数名称：IIC_Stop
	函数功能：IIC 停止信号
===========================================*/
void IIC_Stop(void)
{
	SDA_OUT();			//输出模式

	SDA_LOW();
	SCL_LOW();
	delay_us(2);

	SCL_HIGH();
	delay_us(5);

	SDA_HIGH();			//此时 SDA 一拉高就表示结束了，后面就不用管的
	delay_us(5);
}

/*==========================================
	函数名称：IIC_SendAck
	函数功能：IIC 发送一个ACK  低电平
===========================================*/
static void IIC_SendAck(void)
{
	SDA_OUT();

	SCL_LOW();
	SDA_LOW();
	delay_us(2);

	SCL_HIGH();
	delay_us(5);

	SCL_LOW();			//钳住
}


/*==========================================
	函数名称：IIC_SendNoAck
	函数功能：IIC 发送一个NoSCK  高电平
===========================================*/
static void IIC_SendNoAck(void)
{
	SDA_OUT();

	SCL_LOW();
	SDA_HIGH();
	delay_us(2);

	SCL_HIGH();
	delay_us(5);

	SCL_LOW();
}

/*==========================================
	函数名称：IIC_WaitAck
	函数功能：IIC 等待ACK  低电平
===========================================*/
uint8_t IIC_WaitAck(void)
{
	uint8_t   count = 250;

	SDA_IN();

	SCL_LOW();
	SDA_HIGH();
	delay_us(2);

	SCL_HIGH();
	delay_us(5);

	while (READ_SDA())
	{
		if (!(--count))
		{
			IIC_Stop();
			return NOACK;
		}
	}
	SCL_LOW();
	//delay_us(5);

	return ACK;
}



/*==========================================
	函数名称：IIC_SendByte
	函数功能：IIC 发送信号
===========================================*/
void IIC_SendByte(uint8_t  data)
{
	uint8_t     i;

	SDA_OUT();			//输出模式

	SCL_LOW();
	delay_us(2);

	for (i = 0; i < 8; i++)
	{
		//读取最高位数据
		if (data & 0x80)
			SDA_HIGH();
		else
			SDA_LOW();

		data <<= 1;			//data左移，获取最高位
		delay_us(5);

		SCL_HIGH();
		delay_us(5);		//数据的发送时间

		SCL_LOW();			//钳住
	}
}

/*================================================
	函数名称：IIC_ReadByte
	函数功能：IIC 读取一个字节
	函数参数：ack 读取一个字节，给芯片回复
	返 回 值：读取到的字节数
==================================================*/
uint8_t IIC_ReadByte(uint8_t ack)
{
	uint8_t      i;
	uint8_t      data = 0;

	SDA_IN();

	SCL_LOW();
	SDA_HIGH();
	delay_us(2);

	for (i = 0; i < 8; i++)
	{
		SCL_HIGH();
		data <<= 1;		//循环了8遍，但是最高位只需移动7位，所以为了避免后期数据丢失，先移一位

		if (READ_SDA())
			data |=  0x01;			//进行或操作，接收数据1

		delay_us(5);

		SCL_LOW();
		delay_us(5);
		delay_us(6);
	}

	if (ack)
		IIC_SendNoAck();
	else
		IIC_SendAck();

	return data;
}
